package bx.survey.flutter_bmap;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;

import com.baidu.location.*;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

import java.text.SimpleDateFormat;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/** FlutterBmapPlugin */
public class FlutterBmapPlugin implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  // 通道名称
  private static final String CHANNEL_METHOD_LOCATION = "flutter_bmap";
  private static final String CHANNEL_STREAM_LOCATION = "flutter_bmap_stream";

  private Context mContext = null; // flutter view context
  private LocationClient mLocationClient = null; // 定位对象
  private EventChannel.EventSink mEventSink = null; // 事件对象
  private BDNotifyListener mNotifyListener; // 位置提醒对象

  private boolean isPurposeLoc = false; // 签到场景
  private boolean isInChina = false;  // 是否启用国内外位置判断功能
  private boolean isNotify = false; // 位置提醒

  // 通道对象
  private MethodChannel channel = null;
  private EventChannel eventChannel = null;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    this.mContext = flutterPluginBinding.getApplicationContext();

    //  开始、停止定位
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_METHOD_LOCATION);
    channel.setMethodCallHandler(this);

    //  监听位置变化
    eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_STREAM_LOCATION);
    eventChannel.setStreamHandler(this);

    //channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_bmap");
    //channel.setMethodCallHandler(this);
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if ("startLocation".equals(call.method)) {
      startLocation(); //启动定位
    } else if ("stopLocation".equals(call.method)) {
      stopLocation(); //停止定位
    } else if ("updateOption".equals(call.method)) { //设置定位参数
      try {
        updateOption((Map) call.arguments);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } else if (("getPlatformVersion").equals(call.method)) {
      result.success("Android " + Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
    //if (call.method.equals("getPlatformVersion")) {
    //  result.success("Android " + android.os.Build.VERSION.RELEASE);
    //} else if (call.method.equals("duAddOne")) {
    //  int val = 100;
    //  val += Integer.valueOf(call.argument("num").toString());
    //  result.success(val);
    //} else {
    //  result.notImplemented();
    //}
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
    eventChannel.setStreamHandler(null);
  }

  /**
   * 开始定位
   */
  private void startLocation() {
    if (null != mLocationClient) {
      mLocationClient.start();
    }
  }

  /**
   * 停止定位
   */
  private void stopLocation() {
    if (null != mLocationClient) {
      mLocationClient.stop();
      mLocationClient = null;
    }
  }

  /**
   * 准备定位
   */
  private void updateOption(Map arguments) throws Exception {
    if (null == mLocationClient) {
      LocationClient.setAgreePrivacy(true);
      mLocationClient = new LocationClient(mContext);
    }

    // 判断是否启用位置提醒功能
    if (arguments.containsKey("isNotify")) {
      isNotify = true;
      if (null == mNotifyListener) {
        mNotifyListener = new MyNotifyLister();
      }
      mLocationClient.registerNotify(mNotifyListener);
      double lat = 0;
      double lon = 0;
      float radius = 0;

      if (arguments.containsKey("latitude")) {
        lat = (double) arguments.get("latitude");
      }

      if (arguments.containsKey("longitude")) {
        lon = (double) arguments.get("longitude");
      }

      if (arguments.containsKey("radius")) {
        double radius1 = (double) arguments.get("radius");
        radius = Float.parseFloat(String.valueOf(radius1));
      }

      String coorType = mLocationClient.getLocOption().getCoorType();
      mNotifyListener.SetNotifyLocation(lat, lon, radius, coorType);
      return;
    } else {
      isNotify = false;
    }

    mLocationClient.registerLocationListener(new CurrentLocationListener());

    //判断是否启用国内外位置判断功能
    if (arguments.containsKey("isInChina")) {
      isInChina = true;
      return;
    } else {
      isInChina = false;
    }

    LocationClientOption option = new LocationClientOption();
    parseOptions(option, arguments);
    option.setProdName("flutter");
    mLocationClient.setLocOption(option);
  }

  private void parseOptions(LocationClientOption option, Map arguments) {
    if (arguments != null) {
      //可选，设置是否返回逆向地理信息，默认true
      if (arguments.containsKey("isNeedAddres")) {
        if (((boolean) arguments.get("isNeedAddres"))) {
          option.setIsNeedAddress(true);
        } else {
          option.setIsNeedAddress(false);
        }
      }

      //可选，设置定位模式，可选的模式有高精度、仅设备、仅网络。默认为高精度模式
      if (arguments.containsKey("locationMode")) {
        if (((int) arguments.get("locationMode")) == 1) {
          option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); //高精度模式
        } else if (((int) arguments.get("locationModel")) == 2) {
          option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors); //仅设备模式
        } else if (((int) arguments.get("locationModel")) == 3) {
          option.setLocationMode(LocationClientOption.LocationMode.Battery_Saving); //仅网络模式
        }
      }
    }

    //可选，设备场景定位参数，包括签到场景、运动场景、出行场景
    if ((arguments.containsKey("locationPurpose"))) {
      isPurposeLoc = true;
      if (((int) arguments.get("locationPurpose")) == 1) {
        option.setLocationPurpose(LocationClientOption.BDLocationPurpose.SignIn); //签到场景
      } else if (((int) arguments.get("locationPurpose")) == 2) {
        option.setLocationPurpose(LocationClientOption.BDLocationPurpose.Transport); //运动场景
      } else if (((int) arguments.get("locationPurpose")) == 3) {
        option.setLocationPurpose(LocationClientOption.BDLocationPurpose.Sport); //出行场景
      }
    } else {
      isPurposeLoc = false;
    }

    //可选、设置需要返回海拔高度信息
    if (arguments.containsKey("isNeedAltitude")) {
      if (((boolean) arguments.get("isNeedAltitude"))) {
        option.setIsNeedAltitude(true);
      } else {
        option.setIsNeedAltitude(false);
      }
    }

    //可选，设置是否使用GPS,默认false
    if (arguments.containsKey("openGps")) {
      if ((boolean) arguments.get("openGps")) {
        option.setOpenGps(true);
      } else {
        option.setOpenGps(false);
      }
    }

    //可选，设置是否允许返回逆向地址信息，默认true
    if (arguments.containsKey("isNeedLocationDescribe")) {
      if ((boolean) arguments.get("isNeedLocationDescribe")) {
        option.setIsNeedLocationDescribe(true);
      } else {
        option.setIsNeedLocationDescribe(false);
      }
    }

    //可选，设置发起定位请求的间隔，int类型，单位ms
    // 如果设置为0，则代表单次定位，即仅定位一次，默认为0
    // 如果非0，需设置1000ms以上才有效
    if (arguments.containsKey("scanspan")) {
      Log.d("parseOptions", "parseOptions: " + arguments.get("scanspan").toString());
      option.setScanSpan((int) arguments.get("scanspan"));
    }

    //可选，设置返回经纬度坐标类型，默认GCJ02
    // GCJ02：国测局坐标
    // BD0911:百度经纬度坐标
    // BD09:百度墨卡托坐标
    // 海外地区定位，无需设置坐标类型，统一返回WGS84类型坐标
    if (arguments.containsKey("coorType")) {
      option.setCoorType((String) arguments.get("coorType"));
    }

    //设置是否需要返回附近的poi列表
    if (arguments.containsKey("isNeedLocationPoiList")) {
      if ((boolean) arguments.get("isNeedLocationPoiList")) {
        option.setIsNeedLocationPoiList(true);
      } else {
        option.setIsNeedLocationPoiList(false);
      }
    }

    //设置是否需要最版本rgc数据
    if (arguments.containsKey("isNeedNewVersionRgc")) {
      if ((boolean) arguments.get("isNeedNewVersionRgc")) {
        option.setIsNeedLocationPoiList(true);
      } else {
        option.setIsNeedLocationPoiList(false);
      }
    }
  }

  private String formatUTC(long time, String strPattern) {
    if (TextUtils.isEmpty(strPattern)) {
      strPattern = "yyyy-MM-dd HH:mm:ss";
    }

    SimpleDateFormat sdf = null;
    try {
      sdf = new SimpleDateFormat(strPattern, Locale.CHINA);
      sdf.applyPattern(strPattern);
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return sdf == null ? "NULL" : sdf.format(time);
  }

  @Override public void onListen(Object arguments, EventChannel.EventSink events) {
    mEventSink = events;
  }

  @Override public void onCancel(Object arguments) {
    stopLocation();
    if (isNotify) {
      if (null != mLocationClient) {
        mLocationClient.removeNotifyEvent(mNotifyListener);
      }
      mNotifyListener = null;
    }
  }

  class CurrentLocationListener extends BDAbstractLocationListener {
    @Override public void onReceiveLocation(BDLocation bdLocation) {
      if (null == mEventSink) {
        return;
      }

      Map<String, Object> result = new LinkedHashMap<>();

      // 判断国内外获取结果
      if (isInChina) {
        if (bdLocation.getLocationWhere() == BDLocation.LOCATION_WHERE_IN_CN) {
          result.put("isInChina", 1); //在国内
        } else {
          result.put("isInChina", 0); //在国外
        }
        mEventSink.success(result);
        return;
      }

      // 场景定位获取结果
      if (isPurposeLoc) {
        result.put("latitude", bdLocation.getLatitude()); //纬度
        result.put("longitude", bdLocation.getLongitude()); //经度
        mEventSink.success(result);
        return;
      }

      result.put("callbackTime", formatUTC(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"));
      if (null != bdLocation) {
        Log.d("onReceiveLocation", "onReceiveLocation: " + bdLocation.getLocType());
        if (bdLocation.getLocType() == BDLocation.TypeGpsLocation
            || bdLocation.getLocType() == BDLocation.TypeNetWorkLocation
            || bdLocation.getLocType() == BDLocation.TypeOffLineLocation) {
          result.put("locType", bdLocation.getLocType()); //位置结果类型
          result.put("locTime", bdLocation.getTime()); //定位成功时间
          result.put("latitude", bdLocation.getLatitude()); //纬度
          result.put("longitude", bdLocation.getLongitude()); //经度
          if (bdLocation.hasAltitude()) {
            result.put("altitude", bdLocation.getAltitude()); //高度
          }
          result.put("radius", Double.parseDouble(String.valueOf(bdLocation.getRadius()))); //定位精度
          result.put("country", bdLocation.getCountry()); //国家
          result.put("province", bdLocation.getProvince()); //省份
          result.put("city", bdLocation.getCity()); //城市
          result.put("district", bdLocation.getDistrict()); //区域
          result.put("town", bdLocation.getTown()); //城镇
          result.put("street", bdLocation.getStreet()); //街道
          result.put("address", bdLocation.getAddrStr()); // 地址
          result.put("locationDetail", bdLocation.getLocationDescribe()); //位置语义化描述

          if (null != bdLocation.getPoiList() && !bdLocation.getPoiList().isEmpty()) {
            List<Poi> pois = bdLocation.getPoiList();
            StringBuilder stringBuilder = new StringBuilder();

            if (pois.size() == 1) {
              stringBuilder.append(pois.get(0).getName())
                  .append(",")
                  .append(pois.get(0).getTags())
                  .append(pois.get(0).getAddr());
            } else {
              for (int i = 0; i < pois.size() - 1; i++) {
                stringBuilder.append(pois.get(i).getName())
                    .append(",")
                    .append(pois.get(i).getTags())
                    .append(pois.get(i).getAddr()).append("|");
              }
              stringBuilder.append(pois.get(pois.size() - 1).getName())
                  .append(",")
                  .append(pois.get(pois.size() - 1).getTags())
                  .append(pois.get(pois.size() - 1).getAddr());
            }
            result.put("poiList", stringBuilder.toString()); //周边poi信息
          }

          if (bdLocation.getFloor() != null) {
            //当前支持高精度内定位
            String buildingID = bdLocation.getBuildingID(); //百度内部建筑物ID
            String buildingName = bdLocation.getBuildingName(); //百度内部建筑物缩写
            String floor = bdLocation.getFloor(); //室内定位的楼层信息,如F1,F2,B1,B2
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(buildingID).append("-").append(buildingName).append("-").append(floor);
            result.put("indoor", stringBuilder.toString()); //室内定位结果信息
            mLocationClient.startIndoorMode(); //开启室内定位模式（重复调用也没问题），开启后，定位SDK会融合各种定位信息（GPS,WI-FI，蓝牙，传感器等）连续平滑的输出定位结果
          } else {
            result.put("errorCode", bdLocation.getLocType()); //定位结果错误码
            result.put("errotInfo", bdLocation.getLocTypeDescription()); //定位失败描述信息
          }
        } else {
          result.put("errorCode", -1);
          result.put("errorInfo", "location is null");
        }
        mEventSink.success(result); //Android端实时检测位置变化，将位置结果发送到flutter端
      }
    }
  }

  /**
   * 监听是否到达指定范围附近
   */
  public class MyNotifyLister extends BDNotifyListener {
    //已到达设置监听位置附近
    private void noNotify(BDLocation mlocation, float distance) {
      if (null == mEventSink) {
        return;
      }

      Map<String, Object> result = new LinkedHashMap<>();
      result.put("nearby", "已到达设置监听位置附近"); // 1为已经到达 0为未到达
      mEventSink.success(result);
    }
  }
}
